<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<script type="text/javascript" src="../../d3/d3.min.js" charset="utf-8"></script>
	<script type="text/javascript" src="../../jquery/jquery.min.js" ></script>
	<script type="text/javascript" src="../../jquery/jquery-ui.min.js"></script>
	<script type="text/javascript" src="../../html2canvas/html2canvas.min.js"></script>

	<link rel="stylesheet" href="../../jquery/jquery-ui.min.css" />
	
	<style type="text/css">
	body { 
		background-color: #FAFAFA; 
		margin: 20px;
	}
	
	.NodeCircle {
	    fill: #fff;
	    stroke: steelblue;
	    stroke-width: 3px;
	}
	
	.NodeTitle {
	    font: 11px sans-serif;
	    fill: #000;
	}
	
	.NodeFlag {
	    font-size: 12px;
	    font-weight: bold;
	    fill: black;
	}
	
	.NodeFlagS {
	    font-size: 12px;
	    font-weight: bold;
	    fill: steelblue;
	}
	
	.NodeFlagU {
	    font-size: 12px;
	    font-weight: bold;
	    fill: red;
	}
	
	.NodeFlagR {
	    font-size: 12px;
	    font-weight: bold;
	    fill: green;
	}
	
	.NodeFlagJ {
	    font-size: 12px;
	    font-weight: bold;
	    fill: #9933CC;
	}
	
	.NodeFlagT {
	    font-size: 12px;
	    font-weight: bold;
	    fill: steelblue;
	}
	
	.NodeFlagW {
	    font-size: 12px;
	    font-weight: bold;
	    fill: steelblue;
	}
	
	.NodeFlagC {
	    font-size: 12px;
	    font-weight: bold;
	    fill: red;
	}
	
	.NodeFlagF {
	    font-size: 12px;
	    font-weight: bold;
	    fill: #FF00FF;
	}
	
	.NodeFlagComment {
	    font-size: 12px;
	    fill: gray;
	}
	
	.NodeLineRef {
	    fill: none;
	    stroke: #888;
	    stroke-width: 2px;
	}
	
	.NodeLineFlow {
	    fill: none;
	    stroke: green;
	    stroke-width: 2px;
	}
	
	#tooltip {
	    display: inline-block;
	    position: relative;
	    padding: 0px 15px;
	    border-radius: 5px;
	    border: 1px solid #DDD;
	}
	
	#tooltip table tr td {
		font-size: 12px;
	}
	
	#legend {
		width: 160px; 
		height: 320px; 
		border: 1px solid #888; 
		border-radius: 5px;
		position: fixed;
		right: 20;
		top: 25%;
	}
	
	#title {
		font-size: 14px;
		text-align: center;
	}
	
	#menuDiv {
	    position: fixed;
	}
	
	#menuDiv .ui-menu { 
		width: 150px; 
	}
	</style>
</head>
<body oncontextmenu="return false" class="bodyClass">

	<button type="button" onclick="f_ToBig()">放大</button>
	<button type="button" onclick="f_ToSmall()">缩小</button>
	<button type="button" onclick="f_ToSave()">保存成Png图片</button>
	<a id="saveAHref" href="#">下载</a>
	<div class="svg-wrap">
		<div id="title">xxx的业务流程图</div>
		<svg id="mainSVG" width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
		
			<defs>
				<marker id="arrow" 
				                markerUnits="strokerWidth"
				                markerWidth="16"
				                markerHeight="16"
				                viewBox="0 0 12 12"
				                refX="6"
				                refY="6"
				                orient="auto">
					<path d="M2,2 L10,6 L2,10 L6,6 L2,2" style="fill:green;" />
				</marker>
			</defs>
		
		</svg>
	</div>
	
	<div id="tooltip"></div>
	
	<div id="legend">
		<svg id="legendSVG" width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
		</svg>
	</div>
	
	<div id="menuDiv">
		<ul id="menu">
		  <li class="ui-state-disabled"><div>Toys (n/a)</div></li>
		  <li><div>Books</div></li>
		  <li><div>Clothing</div></li>
		  <li><div>Electronics</div>
		    <ul>
		      <li class="ui-state-disabled"><div>Home Entertainment</div></li>
		      <li><div>Car Hifi</div></li>
		      <li><div>Utilities</div></li>
		    </ul>
		  </li>
		  <li><div>Movies</div></li>
		  <li><div>Music</div>
		    <ul>
		      <li><div>Rock</div>
		        <ul>
		          <li><div>Alternative</div></li>
		          <li><div>Classic</div></li>
		        </ul>
		      </li>
		      <li><div>Jazz</div>
		        <ul>
		          <li><div>Freejazz</div></li>
		          <li><div>Big Band</div></li>
		          <li><div>Modern</div></li>
		        </ul>
		      </li>
		      <li><div>Pop</div></li>
		    </ul>
		  </li>
		  <li class="ui-state-disabled"><div>Specials (n/a)</div></li>
		</ul>
	</div>
	
	
	<script type="text/javascript">
		var v_CircleSize   = 8;       /* 节点上圆的大小 */
		var v_Width        = 1024;    /* 画布宽度 */
		var v_Height       = 1000;    /* 画布高度 */
		var v_LevelSpacing = 128;     /* 不同层级间的间隔 */
		var v_NodeSpacing  = 50;      /* 同一父节点的多个子节点间的间隔 */
		var v_JsonDatas    = null;    /* 整个Json数据 */
		var v_MenuJson     = null;    /* 右键菜单选中的Json数据 */
		var v_SVG          = d3.select("body").select("#mainSVG").append("g");
		var v_LegendSVG    = d3.select("body").select("#legendSVG");
		var v_CircleZoom   = d3.zoom().scaleExtent([0.5 ,2])
		.on("zoom" ,function()
	    {
			console.log(d3.select(this).attr("transform"));
			d3.select(this).attr("transform" ,d3.select(this).attr("transform") + " scale(" + d3.event.transform.k + ")"); 
		});
		
		
		
		
		
		
		/**
		 * 返回结果是数组类型，元素0是：共有多少个叶子节点，元素1是：最深的层级是多少。
		 *
		 * @author      ZhengWei(HY)
		 * @createDate  2018-09-09
		 * @version     v1.0
		 * 
		 * @param i_Json  树结构的Json数据
		 * @param i_Level 递归遍历树结构的层级。下标从1开始
		 */
		function getLeafNodeCount(i_Json ,i_Level)
		{
			if ( i_Json.children == null || i_Json.children.length <= 0 )
			{
				return [1 ,i_Level];
			}
			
			var v_Ret = [0 ,i_Level];
			i_Json.children.forEach(function(d)
			{
				if ( d.hidden == null || !d.hidden )
				{
					var v_ChildrenRet = getLeafNodeCount(d ,i_Level + 1);
					
					v_Ret[0] = v_Ret[0] + v_ChildrenRet[0];
					v_Ret[1] = d3.max([v_Ret[1] ,v_ChildrenRet[1]]);
				}
			});
			
			return v_Ret;
		}
		
		
		
		/**
		 * 递归计算树结构
		 *
		 * @author      ZhengWei(HY)
		 * @createDate  2018-09-09
		 * @version     v1.0
		 *
		 * @param i_Json          树结构的Json数据
		 * @param i_Level         递归遍历树结构的层级。下标从1开始
		 * @param i_Index         同一父节点的多个子节点的排列序号，下标从1开始
		 * @param i_Size          同一父节点的多个子节点的数量
		 * @param i_TreeLeafIndex 整个树结构叶子节点的序号，下标从1开始
		 * @param i_SuperJson     父节点的Json数据
		 * @param i_PreviousJson  同一父节点的上一个子节点的Json数据 
		 */
		function calcTreeForEach(i_Json ,i_Level ,i_Index ,i_Size ,i_TreeLeafIndex ,i_SuperJson ,i_PreviousJson)
		{
			if ( i_SuperJson == null )
			{
				i_Json.id = "NID-" + i_Index;
				i_Json.x  = v_NodeSpacing;
				i_Json.y  = v_NodeSpacing;
			}
			else
			{
				i_Json.id = i_SuperJson.id + "-" + i_Index;
				i_Json.sx = i_SuperJson.x;
				i_Json.sy = i_SuperJson.y;
				i_Json.x  = i_SuperJson.x + v_LevelSpacing;
				i_Json.y  = i_TreeLeafIndex * v_NodeSpacing;
			}
			
			if ( i_Json.children == null || i_Json.children.length <= 0 )
			{
				return [i_Json ,i_TreeLeafIndex + 1];
			}
			
			var v_Index         = 1;
			var v_PreviousJson  = i_Json;
			var v_TreeLeafIndex = i_TreeLeafIndex;
			var v_ShowCount     = 0;
			i_Json.children.forEach(function(d)
			{
				if ( d.hidden == null || !d.hidden )
				{
					v_ShowCount++;
					d.px = v_PreviousJson.x;
					d.py = v_PreviousJson.y;
					var v_CalcRet = calcTreeForEach(d ,i_Level + 1 ,v_Index++ ,i_Json.children.length ,v_TreeLeafIndex ,i_Json ,v_PreviousJson);
					v_PreviousJson  = v_CalcRet[0];
					v_TreeLeafIndex = v_CalcRet[1];
				}
			});
			
			i_Json.showCount = v_ShowCount;
			
			if ( v_ShowCount <= 0 )
			{
				/* 没有一个子节点，或所有子节点被隐藏了 */
				return [v_PreviousJson ,v_TreeLeafIndex + 1];
			}
			else
			{
				return [v_PreviousJson ,v_TreeLeafIndex];
			}
		}
		
		
		
		/**
		 * 递归绘制树结构中的节点
		 *
		 * @author      ZhengWei(HY)
		 * @createDate  2018-09-09
		 * @version     v1.0
		 *
		 * @param i_Json  树结构的Json数据
		 */
		function drawTreeForEachByNode(i_Json)
		{
			drawNode(i_Json);
			
			if ( i_Json.children == null || i_Json.children.length <= 0 )
			{
				return;
			}
			
			i_Json.children.forEach(function(d)
			{
				if ( d.hidden == null || !d.hidden )
				{
					drawTreeForEachByNode(d);
				}
			});
		}
		
		
		
		/**
		 * 递归绘制树结构中的连接线
		 *
		 * @author      ZhengWei(HY)
		 * @createDate  2018-09-09
		 * @version     v1.0
		 *
		 * @param i_Json  树结构的Json数据
		 */
		function drawTreeForEachByLine(i_Json)
		{
			drawLine(i_Json);
			
			if ( i_Json.children == null || i_Json.children.length <= 0 )
			{
				return;
			}
			
			i_Json.children.forEach(function(d)
			{
				if ( d.hidden == null || !d.hidden )
				{
					drawTreeForEachByLine(d);
				}
			});
		}
		
		
		
		/**
		 * 绘制节点
		 *
		 * @author      ZhengWei(HY)
		 * @createDate  2018-09-09
		 * @version     v1.0
		 *
		 * @param i_Json  树结构的Json数据
		 */
		function drawNode(i_Json)
		{
			var v_G = v_SVG.append("g")
			.attr("id" ,i_Json.id)
			.attr("transform" ,"translate(" + i_Json.x + "," + i_Json.y + ")")
			.on("click" ,function()
			{
				if ( i_Json.children != null && i_Json.children.length >= 1 )
				{
					i_Json.children.forEach(function(d)
					{
						d.hidden = i_Json.showCount >= 1
					});
					
					drawTree(v_JsonDatas);
				}
			})
			.on("contextmenu" ,function()
			{
				var v_MenuDiv = d3.select("body").select("#menuDiv")
				.style("left" ,(d3.event.pageX - 170) + "px")     
				.style("top"  ,(d3.event.pageY - 28) + "px");
			})
			.on("mouseover" ,function() 
			{         
				v_Tooltip.html(makeTooltipText(i_Json))  
				.style("left" ,(d3.event.pageX + (v_LevelSpacing/2)) + "px")     
				.style("top"  ,(d3.event.pageY - 28) + "px")
				.style("opacity" ,100);
				
				console.log("dddddddddddddddeeeeeeeeeee");
				v_CircleZoom.scaleTo(d3.select(this) ,1.25);
			})
			.on("mouseout" ,function() 
			{
				v_Tooltip
				.style("left" ,"-1000px")     
				.style("top"  ,"-1000px")
				.style("opacity" ,0);
				v_CircleZoom.scaleTo(v_G ,0.8);
			});
			
			var v_Circle = v_G.append("circle")
			.attr("class" ,"NodeCircle")
			.attr("r" ,v_CircleSize);
			
			var v_Title = v_G.append("text")
			.attr("class" ,"NodeText")
			.attr("dy" ,v_CircleSize * -1 - 3)
			.attr("text-anchor" ,"middle")
			.text(i_Json.name);
		}
		
		
		
		/**
		 * 绘制连线
		 *
		 * @author      ZhengWei(HY)
		 * @createDate  2018-09-09
		 * @version     v1.0
		 *
		 * @param i_Json  树结构的Json数据
		 */
		function drawLine(i_Json)
		{
			if ( i_Json.sx != null )
			{
				var v_OfferX = 0;
				var v_OfferY = 0;
				
				if ( i_Json.px == i_Json.x && i_Json.py != i_Json.y )
				{
					/* 画的是垂直线，所以y轴偏移，跳过节点高度 */
					v_OfferY = v_CircleSize * -2;
				}
				else if ( i_Json.px != i_Json.x && i_Json.py == i_Json.y )
				{
					/* 画的是水平线，所以x轴偏移，路过节点宽度 */
					v_OfferX = v_CircleSize * -2;
				}
				else
				{
					v_OfferX = v_CircleSize * 1.5;
					v_OfferY = v_CircleSize * -0.75;
				}
				
				var v_Line = v_SVG.append("line")
				.attr("class" ,"NodeLineFlow")
				.attr("marker-end" ,"url(#arrow)")
				.attr("x1" ,i_Json.px)
				.attr("y1" ,i_Json.py)
				.attr("x2" ,i_Json.x + v_OfferX)
				.attr("y2" ,i_Json.y + v_OfferY);
				
				
				/* 方案2：画常规树目录的折线连接 */
				var v_Line = v_SVG.append("path")
				.attr("class" ,"NodeLineRef")
				.attr("stroke-dasharray" ,"2,5")
				.attr("d" ,"M" + i_Json.sx + "," + i_Json.sy 
						+ " V" + i_Json.y
						+ " H" + (i_Json.x - v_CircleSize));
				
				/* 方案1：画直线连接 */
				/*
				var v_Line = v_SVG.append("line")
				.attr("class" ,"NodeLineRef")
				.attr("stroke-dasharray" ,"2,5")
				.attr("x1" ,i_Json.sx)
				.attr("y1" ,i_Json.sy)
				.attr("x2" ,i_Json.x)
				.attr("y2" ,i_Json.y);
				*/
			}
		}
		
		
		
		/**
		 * 绘制图例
		 *
		 * @author      ZhengWei(HY)
		 * @createDate  2018-09-11
		 * @version     v1.0
		 */
		function drawLegends()
		{
			var v_Nodes = [{"flag":" "  ,"comment":"组、节点。有右击菜单"}
			              ,{"flag":"S"  ,"comment":"查询数据库节点"} 
			              ,{"flag":"U"  ,"comment":"更新数据库节点"} 
			              ,{"flag":"J"  ,"comment":"执行Java方法的节点"} 
			              ,{"flag":"C"  ,"comment":"云计算节点"} 
			              ,{"flag":"T"  ,"comment":"并行计算节点"} 
			              ,{"flag":"W"  ,"comment":"并行等待"} 
			              ,{"flag":"R"  ,"comment":"缓存集合遍历节点"}
			              ,{"flag":"F"  ,"comment":"组递归循环"}];
			
			var v_Lines = [{"className":"NodeLineRef"  ,"dasharray":"2,5" ,"markerEnd":""            ,"comment":"组及节点的关系线"}
			              ,{"className":"NodeLineFlow" ,"dasharray":""    ,"markerEnd":"url(#arrow)" ,"comment":"业务走势线"}
			              ,{"className":"NodeLineFlow" ,"dasharray":"5,2" ,"markerEnd":"url(#arrow)" ,"comment":"有条件的业务走势线"}];
			
			v_LegendSVG.selectAll(".legendNode")
			.data(v_Nodes)
			.enter()
			.append("g")
			.attr("class" ,"legendNode")
			.attr("transform" ,function(d ,i)
			{
				return "translate(" + 20 + "," + ((v_CircleSize * 2) * i + (10 * (i + 1)) + 10) + ")";
			})
			.each(function(d ,i)
			{
				drawLegendNode(d3.select(this) ,d.comment ,d.flag);
			});
			
			v_LegendSVG.selectAll(".legendLine")
			.data(v_Lines)
			.enter()
			.append("g")
			.attr("class" ,"legendLine")
			.attr("transform" ,function(d ,i)
			{
				return "translate(" + 5 + "," + ((v_CircleSize * 2) * v_Nodes.length + (10 * (v_Nodes.length + 1)) + 10 + i * v_CircleSize * 2 + i * 5) + ")";
			})
			.each(function(d ,i)
			{
				drawLegendLine(d3.select(this) ,d);
			});
		}
		drawLegends();
		
		
		
		/**
		 * 绘制图例中的线
		 *
		 * @author      ZhengWei(HY)
		 * @createDate  2018-09-11
		 * @version     v1.0
		 *
		 * @param i_G     G元素
		 * @param i_Data  数据{"className":""  ,"dasharray":"" ,"markerEnd":"" ,"comment":""}
		 */
		function drawLegendLine(i_G ,i_Data)
		{
			var v_Line = i_G.append("path")
			.attr("class" ,i_Data.className)
			.attr("d" ,"M0,5 H20");
			
			if ( i_Data.dasharray != "" )
			{
				v_Line.attr("stroke-dasharray" ,i_Data.dasharray);
			}
			
			if ( i_Data.markerEnd != "" )
			{
				v_Line.attr("marker-end" ,i_Data.markerEnd);
			}
			
			var v_Comment = i_G.append("text")
			.attr("class" ,"NodeFlagComment")
			.attr("dx" ,30)
			.attr("dy" ,10)
			.text(i_Data.comment);
		}
		
		
		
		/**
		 * 绘制图例中的节点
		 *
		 * @author      ZhengWei(HY)
		 * @createDate  2018-09-11
		 * @version     v1.0
		 *
		 * @param i_G          G元素
		 * @param i_Comment    说明文字
		 * @param i_FlagValue  圆内文字
		 */
		function drawLegendNode(i_G ,i_Comment ,i_FlagValue)
		{
			drawNodeCircleFlag(i_G ,i_FlagValue);
			
			var v_Comment = i_G.append("text")
			.attr("class" ,"NodeFlagComment")
			.attr("dx" ,v_CircleSize * 2)
			.attr("dy" ,5)
			.text(i_Comment);
		}
		
		
		
		/**
		 * 绘制节点圆及圆内文字
		 *
		 * @author      ZhengWei(HY)
		 * @createDate  2018-09-11
		 * @version     v1.0
		 *
		 * @param i_G          G元素
		 * @param i_FlagValue  圆内文字
		 */
		function drawNodeCircleFlag(i_G ,i_FlagValue)
		{
			var v_Circle = i_G.append("circle")
			.attr("class" ,"NodeCircle")
			.attr("r" ,v_CircleSize);
			
			var v_Flag = i_G.append("text")
			.attr("class" ,"NodeFlag" + i_FlagValue)
			.attr("dy" ,v_CircleSize / 2 + 1)
			.attr("text-anchor" ,"middle")
			.text(i_FlagValue);
		}
		
		
		
		/**
		 * 绘制树结构（对外主调方法）
		 *
		 * @author      ZhengWei(HY)
		 * @createDate  2018-09-09
		 * @version     v1.0
		 *
		 * @param i_Json  树结构的Json数据
		 */
		function drawTree(i_Json)
		{
			v_SVG.selectAll(".NodeCircle").remove();
			v_SVG.selectAll(".NodeText").remove();
			v_SVG.selectAll(".NodeLineRef").remove();
			v_SVG.selectAll(".NodeLineFlow").remove();
			
			calcTreeForEach(i_Json ,1 ,1 ,1 ,1 ,null ,null);
			drawTreeForEachByLine(i_Json);
			drawTreeForEachByNode(i_Json);
		}
		
		
		
		/**
		 * 生成节点提示内容
		 *
		 * @author      ZhengWei(HY)
		 * @createDate  2018-09-09
		 * @version     v1.0
		 *
		 * @param i_Json  树结构的Json数据
		 */
		function makeTooltipText(i_Json)
		{
			var v_Text = "";
			
			v_Text += "<p><table>";
			
			v_Text += "<tr>";
			v_Text += "<td>name</td>";
			v_Text += "<td>" + i_Json.name + "</td>";
			v_Text += "</tr>";
			
			v_Text += "<tr>";
			v_Text += "<td>id</td>";
			v_Text += "<td>" + i_Json.id + "</td>";
			v_Text += "</tr>";
			
			v_Text += "</table></p>";
			
			return v_Text
		}
		
		
		
		var v_Tooltip = d3.select("#tooltip")
		.style("position" ,"absolute")
		.style("z-index"  ,"99999");
		
		
		d3.json("009_Tree.json").then(function(i_Json)
		{
			console.log("读取开始");
			
			v_JsonDatas = i_Json;
			drawTree(v_JsonDatas);
			
			var v_LeafNodeCount = getLeafNodeCount(v_JsonDatas ,1);
			console.log(v_LeafNodeCount[1]);
			v_Width  = (v_LeafNodeCount[1] + 2) * v_LevelSpacing;
			v_Height = (v_LeafNodeCount[0] + 2) * v_NodeSpacing;
			v_SVG
			.attr("width"  ,v_Width)
			.attr("height" ,v_Height);
			
			console.log("读取完成");
		});
		
		
		var v_Zoom = d3.zoom()
        .scaleExtent([0.5 ,2])
        .on("zoom" ,function()
        {
        	d3.select(this).attr("transform", "translate(" + d3.event.transform.x + "," + d3.event.transform.y + ") scale(" + d3.event.transform.k + ")");
        });

		d3.select("#mainSVG").call(v_Zoom);
		
		
		var v_ZoomValue = 1.0;
		function f_ToBig()
		{
			v_ZoomValue = v_ZoomValue + 0.2;
			v_Zoom.scaleTo(v_SVG ,v_ZoomValue);
		}
		
		
		
		function f_ToSmall()
		{
			v_ZoomValue = v_ZoomValue - 0.2;
			v_Zoom.scaleTo(v_SVG ,v_ZoomValue);
		}
		
		
		
		function toCanvas(svgData, width, height, callback) 
		{
			  var src = 'data:image/svg+xml;charset=utf-8;base64,' + svgData;
			  var canvas = document.createElement('canvas');
			  var context = canvas.getContext('2d');
			  var image = new window.Image();
			  canvas.width = width;
			  canvas.height = height;
			  image.onload = function () {
			    
			    callback(canvas);
			  };
			  image.src = src;
			  
			  context.drawImage(image, 0, 0);
			  
			  console.log(src);
			  
			  d3.select("#saveAHref").
			  attr("download" ,"123.png")
			  .attr("href" ,canvas.toDataURL('image/png'));
		};
		
		
		
		function f_ToSave()
		{
			html2canvas(document.body).then(canvas => {
			    document.body.appendChild(canvas)
			});
		}
		
		
		
		$( function() 
		{
			$("#menu").menu();
		});
	</script>

</body>
</html>